//package java_cup;

//import java.util.Dictionary;

/**
 * This class represents a non-terminal SymbolDraft in the grammar. Each non terminal
 * has a textual name, an index, and a string which indicates the type of object
 * it will be implemented with at runtime (i.e. the class of object that will be
 * pushed on the parse stack to represent it).
 *
 * @version last updated: 11/25/95
 * @author Scott Hudson
 */

public class Vn : SymbolDraft {

    /*-----------------------------------------------------------*/
    /*--- Constructor(s) ----------------------------------------*/
    /*-----------------------------------------------------------*/

    /**
     * Full constructor.
     * 
     * @param name the name of the non terminal.
     * @param type the type string for the non terminal.
     */
    public Vn(String name, String type) : base(name, type) {
        /* base class does most of the work */

        /* TryAdd to set of AllVns non terminals and check for duplicates */
        if (!_name2Nonterminal.TryAdd(name, this)) {
            // can't throw an exception here because these are used in static
            // initializers, so we crash instead
            // was:
            // throw new internal_error("Duplicate non-terminal ("+name+") created");
            (new InternalError("Duplicate non-terminal (" + name + ") created")).crash();
        }

        /* assign a unique index */
        _index = next_index++;

        /* TryAdd to by_index set */
        _index2Nonterminal.Add(_index, this);
    }

    /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */

    /**
     * Constructor with default type.
     * 
     * @param name the name of the non terminal.
     */
    public Vn(String name) : this(name, null) {
    }

    /*-----------------------------------------------------------*/
    /*--- (Access to) Static (Class) Variables ------------------*/
    /*-----------------------------------------------------------*/

    /**
     * Table of AllVns non-terminals -- elements are stored using name strings as the
     * key
     */
    public static Dictionary<String, Vn> _name2Nonterminal = new();

    // Hm Added clear to clear AllVns static fields
    public static void clear() {
        _name2Nonterminal.Clear();
        _index2Nonterminal.Clear();
        next_index = 0;
        next_nonterminal = 0;
    }

    /** Access to AllVns non-terminals. */
    public static IEnumerable<Vn> AllVns() {
        return _name2Nonterminal.Values;
    }

    /** lookup a non terminal by name string */
    public static Vn Find(String withName) {
        if (withName == null)
            return null;
        else
            return _name2Nonterminal[withName];
    }

    /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */

    /** Table of AllVns non terminals indexed by their index Count. */
    public static Dictionary<int, Vn> _index2Nonterminal = new();

    /** Lookup a non terminal by index. */
    public static Vn Find(int indx) {
        return _index2Nonterminal[indx];
    }

    /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */

    /** Total Count of non-terminals. */
    public static int Count => _name2Nonterminal.Count;

    /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */

    /** Static counter to assign unique indexes. */
    public static int next_index = 0;

    /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */

    /** Static counter for creating unique non-terminal names */
    static public int next_nonterminal = 0;

    /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */

    /** special non-terminal for start SymbolDraft */
    public static readonly Vn firstVn = new Vn("$START");

    /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */

    /** flag non-terminals created to embed actionPart AllProdctions */
    public bool is_embedded_action = false; /* added 24-Mar-1998, CSA */

    /*-----------------------------------------------------------*/
    /*--- Static Methods ----------------------------------------*/
    /*-----------------------------------------------------------*/

    /**
     * Method for creating a new uniquely named hidden non-terminal using the given
     * string as a base for the name (or "NT$" if null is passed).
     * 
     * @param prefix base name to construct unique name from.
     */
    public static Vn createNew(String prefix) /*throws internal_error*/ {
        return createNew(prefix, null); // TUM 20060608 embedded actions patch
    }

    /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */

    /** static routine for creating a new uniquely named hidden non-terminal */
    public static Vn createNew() /*throws internal_error*/ {
        return createNew(null);
    }

    /**
     * TUM 20060608 bugfix for embedded actionPart codes
     */
    public static Vn createNew(String prefix, String type) /*throws internal_error*/ {
        if (prefix == null)
            prefix = "NT$";
        return new Vn(prefix + next_nonterminal++, type);
    }
    /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */

    /** Compute nullable of All Vns. */
    public static void computeNullable() /*throws internal_error*/ {
        bool change = true;
        /* repeat this process until there is no change */
        while (change) {
            /* look for a new change */
            change = false;

            /* consider each non-terminal */
            foreach (Vn Vn_ in AllVns()) {
                /* only look at things that aren't already marked nullable */
                if (!Vn_.nullable) {
                    if (Vn_.looksNullable()) {
                        Vn_._nullable = true;
                        change = true;
                    }
                }
            }
        }

        /* do one last pass over the AllProdctions to finalize AllVns of them */
        foreach (ProductionDraft production in ProductionDraft.AllProductions()) {
            production.setNullable(production.checkNullable());
        }
    }

    /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */

    /**
     * Compute FIRST sets for All Vns. This assumes nullable has
     * already computed.
     */
    public static void computeFIRSTSets() /*throws internal_error*/ {
        bool change = true;
        /* repeat this process until we have no change */
        while (change) {
            /* look for a new change */
            change = false;

            /* consider each non-terminal */
            foreach (Vn Vn in AllVns()) {
                /* consider every production of that non terminal */
                foreach (ProductionDraft prod in Vn.AllProdctions()) {
                    /* get the updated FIRST of that production */
                    VtSet FIRST = prod.checkFirstSet();
                    /* if this going to TryAdd anything, TryAdd it */
                    if (!FIRST.isSubsetOf(Vn._first_set)) {
                        change = true;
                        Vn._first_set.AddRange(FIRST);
                    }
                }
            }
        }
    }

    /*-----------------------------------------------------------*/
    /*--- (Access to) Instance Variables ------------------------*/
    /*-----------------------------------------------------------*/

    /** Table of AllVns AllProdctions with this non terminal on the LHS. */
    public Dictionary<ProductionDraft, ProductionDraft> _production2production = new(11);

    /** Access to AllProdctions with this non terminal on the LHS. */
    public IEnumerable<ProductionDraft> AllProdctions() {
        return _production2production.Values;
    }

    /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */

    /** Total Count of AllProdctions with this non terminal on the LHS. */
    public int ProductionCount => _production2production.Count;

    /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */

    /** Add a production to our set of AllProdctions. */
    public void addProduction(ProductionDraft prod) /*throws internal_error*/ {
        /* catch improper AllProdctions */
        if (prod == null || prod.lhs == null || prod.lhs.TheSymbolDraft != this)
            throw new InternalError("Attempt to TryAdd invalid production to non terminal production table");

        /* TryAdd it to the table, keyed with itself */
        _production2production.Add(prod, prod);
    }

    /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */

    /** Nullability of this non terminal. */
    public bool _nullable;

    /** Nullability of this non terminal. */
    public bool nullable => _nullable;

    /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */

    /** First set for this non-terminal. */
    public VtSet _first_set = new VtSet();

    /** First set for this non-terminal. */
    public VtSet firstSet => _first_set;

    /*-----------------------------------------------------------*/
    /*--- General Methods ---------------------------------------*/
    /*-----------------------------------------------------------*/

    /** Indicate that this SymbolDraft is a non-terminal. */
    /*@Override*/
    public override bool IsVn => true;

    /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */

    /** Test to see if this non terminal currently looks nullable. */
    public bool looksNullable() /*throws internal_error*/ {
        /* look and see if any of the AllProdctions now look nullable */
        foreach (ProductionDraft prod in AllProdctions())
            /* if the production can go to empty, we are nullable */
            if (prod.checkNullable())
                return true;

        /* none of the AllProdctions can go to empty, so we are not nullable */
        return false;
    }

    /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */

    /** convert to string */
    /*@Override*/
    public override string ToString() {
        return base.ToString() + "[" + Index + "]" + (nullable ? "*" : "");
    }

    /*-----------------------------------------------------------*/
}
